﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Windows.Data;
using System.Globalization;
using System.Windows.Markup;
using System.Windows;

namespace DotNetZen.Presentation.Converters
{
    /// <summary>
    /// Represents a folding strategy for a <see cref="MultiBindingFoldingConverter"/>.
    /// </summary>
    [ContentProperty("BindingReferences")]
    public class Fold : Evaluator
    {
        #region IMultiValueConverter Converter { get; set; } (converter)
        private IMultiValueConverter converter;

        /// <summary>
        /// Gets or sets the converter used to fold the laundry.
        /// </summary>
        [System.ComponentModel.Description("Indicates the converter used to fold the laundry.")]
        public IMultiValueConverter Converter
        {
            [System.Diagnostics.DebuggerStepThrough]
            get
            {
                return this.converter;
            }
            [System.Diagnostics.DebuggerStepThrough]
            set
            {
                if (this.converter != value)
                {
                    this.converter = value;
                }
            }
        }
        #endregion

        #region List<Evaluator> BindingReferences { get; } (bindingReferences)
        private readonly List<Evaluator> bindingReferences = new List<Evaluator>();

        public List<Evaluator> BindingReferences
        {
            [System.Diagnostics.DebuggerStepThrough]
            get
            {
                return this.bindingReferences;
            }
        }
        #endregion

        [DebuggerStepThrough]
        protected internal override sealed object ConvertCore(object[] values, CultureInfo info)
        {
            return this.ConvertCore(values, typeof(object), info);
        }

        protected internal virtual object ConvertCore(object[] values, Type targetType, CultureInfo info)
        {
            // Check to see that values is not null
            if (values == null)
                throw new ArgumentNullException("values");

            // Check to see that converter is not null
            if (this.converter == null)
                throw new ArgumentNullException("Converter", "Can not fold with no converter.");

            object[] newValues = new object[this.bindingReferences.Count];

            for (int index = 0; index < newValues.Length; index++)
            {
                newValues[index] = this.bindingReferences[index].Convert(values, info);

                // Error encountered at a lower level.
                if (newValues[index] == DependencyProperty.UnsetValue)
                    return DependencyProperty.UnsetValue;
            }

            return this.converter.Convert(newValues, targetType, this.ConverterParameter, info);
        }

        [DebuggerStepThrough]
        internal object Convert(object[] values, Type targetType, CultureInfo info)
        {
            return this.ConvertCore(values, targetType,
                this.ConverterCulture ?? info);
        }

        protected internal override void ConvertBackCore(object value, Type[] targetTypes, CultureInfo info, object[] returnedItems)
        {
            // Check to see that converter is not null
            if (this.converter == null)
                throw new ArgumentNullException("Converter", "Can not fold with no converter.");

            object[] converterValues = this.converter.ConvertBack(value, 
                new Type[this.bindingReferences.Count].FillWith(typeof(object)),
                this.ConverterParameter,
                (this.ConverterCulture ?? info));

            for (int index = 0; index < this.bindingReferences.Count; index++)
            {
                Evaluator evaluator = this.bindingReferences[index];

                evaluator.ConvertBack(converterValues[index], targetTypes, info, returnedItems);
            }
        }
    }
}
